package com.songaw.generator.modules.auths.service.impl;

import com.songaw.generator.common.conf.redis.RedisUtil;
import com.songaw.generator.common.constant.Constant;
import com.songaw.generator.common.exception.UnauthorizedException;
import com.songaw.generator.common.pojo.dto.Result;
import com.songaw.generator.modules.auths.entity.User;
import com.songaw.generator.modules.auths.mapper.SecurityExMapper;
import com.songaw.generator.modules.auths.pojo.dto.AuthUserDto;
import com.songaw.generator.modules.auths.pojo.dto.JwtLoginDto;
import com.songaw.generator.modules.auths.pojo.dto.MenuDto;
import com.songaw.generator.modules.auths.pojo.dto.UserDto;
import com.songaw.generator.modules.auths.pojo.vo.AddUserVo;
import com.songaw.generator.modules.auths.pojo.vo.JwtLoginVo;
import com.songaw.generator.modules.auths.security.JwtTokenUtil;
import com.songaw.generator.modules.auths.service.AuthService;
import com.songaw.generator.modules.auths.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCrypt;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.stream.Collectors;

/**
 * TODO
 *
 * @author songaw
 * @date 2018/7/27 16:01
 */
@Slf4j
@Service
public class AuthServiceImpl implements AuthService,UserDetailsService{

    @Autowired
    SecurityExMapper securityExMapper;
    @Autowired
    UserService userService;
    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;


    @Value("${jwt.header}")
    private String tokenHeader;

    @Value("${jwt.tokenHead}")
    private String tokenHead;
    //    token失效时间为5分钟
    @Value("${jwt.expiration}")
    private Long expiration;
    //token失效后 中间间隔2分钟是有效的
    @Value("${jwt.refresh_token_expiration}")
    private Long refreshTokenExpiration;
    //redis中token失效时间为1小时
    @Value("${jwt.redis_token_expiration}")
    private Long redisTokenExpiration;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        AuthUserDto authUserDto= securityExMapper.loadUserByUsername(username);


        if(authUserDto!=null){
            return authUserDto;
        }else{
            throw new UsernameNotFoundException("用户["+username+"]不存在");
        }
    }
    public Result register(AddUserVo vo){
        if(StringUtils.isNotBlank(vo.getUserName())){
            User user = new User();
            user.setUserName(vo.getUserName());
            List<User> list= userService.select(user);
            if(!CollectionUtils.isEmpty(list)&&list.size()>0){

                return Result.getSystemErrorResult("该用户名已存在!");

            }
        }

        User user = new User();
        BeanUtils.copyProperties(vo,user);
        String hash = BCrypt.hashpw(user.getPassword(), BCrypt.gensalt());
        user.setPassword(hash);
        userService.insert(user);
        UserDto userDto = new UserDto();
        BeanUtils.copyProperties(user,userDto);
        if(RedisUtil.hasKey(Constant.CODE_CACHE_USERNAME+vo.getUserName())){
            RedisUtil.del(Constant.CODE_CACHE_USERNAME+vo.getUserName());
        }
        return Result.getSuccessResult(userDto);
    }
    public Result login(JwtLoginVo authenticationRequest){
     /*   UserAuthCode userAuthCode=null;
        try {
            //校验验证码
            userAuthCode = RedisUtil.get(CacheConstant.USER_KEY_VERICODE+authenticationRequest.getUsername(), UserAuthCode.class);
            userAuthCode = userAuthCode == null ? new UserAuthCode() : userAuthCode;
            if (!StringUtils.isEmpty(userAuthCode.getVeriCode())) {
                if (authenticationRequest.getVeriCode() == null || !userAuthCode.getVeriCode().toUpperCase().equals(authenticationRequest.getVeriCode().toUpperCase())) {
                    return Result.getCodeLoginErrResult("验证码错误!");

                }
            }
        }catch (RedisConnectionFailureException e){
            log.error("redis error");
        }*/

        UsernamePasswordAuthenticationToken upToken = new UsernamePasswordAuthenticationToken(authenticationRequest.getUsername(), authenticationRequest.getPassword());
        Authentication authentication =null;
        try {
            authentication=  authenticationManager.authenticate(upToken);
           /* if(authentication!=null) {
                RedisUtil.del(CacheConstant.USER_KEY_VERICODE + authenticationRequest.getUsername());
            }*/
        }catch (AuthenticationException e){
            //记录用户登录失败次数
          /*  RLock lock = RedisUtil.getRedisson().getLock("LOCK" + CacheConstant.USER_KEY_VERICODE+authenticationRequest.getUsername());
            try{
                if (lock!=null&&lock.tryLock(200, TimeUnit.SECONDS)) {
                    try {
                        log.info("获得锁");
                        userAuthCode = RedisUtil.get(CacheConstant.USER_KEY_VERICODE+authenticationRequest.getUsername(), UserAuthCode.class);
                        userAuthCode = userAuthCode == null ? new UserAuthCode() : userAuthCode;
                        LimitQueue<String> limitQueue = userAuthCode.getLimitQueue();
                        if (limitQueue == null) {
                            limitQueue = new LimitQueue<String>(2);
                        }
                        //记录当前时间
                        limitQueue.offer(DateUtil.getCurrenTime());
                        userAuthCode.setLimitQueue(limitQueue);
                        //TODO 如果不行则注释掉
                        userAuthCode.setVeriCode(DateUtil.getCurrenTime());
                        RedisUtil.set(CacheConstant.USER_KEY_VERICODE+authenticationRequest.getUsername(), userAuthCode);
                    } catch (RedisConnectionFailureException e3) {
                        log.error("redis error");
                    } finally {
                        log.info("解除锁");
                        lock.unlock();
                    }
                }
            }catch (Exception e2){
                e2.printStackTrace();
            }*/
            throw e;
        }
        if(authentication!=null) {
            SecurityContextHolder.getContext().setAuthentication(authentication);
            AuthUserDto userDetails = (AuthUserDto) authentication.getPrincipal();
            final String token = jwtTokenUtil.generateToken(userDetails.getUsername());

            JwtLoginDto jwtLoginDto = new JwtLoginDto(token, userDetails.getId(), userDetails.getUsername(), userDetails.getMenus());
            return Result.getSuccessResult(jwtLoginDto);
        }else{
            throw new BadCredentialsException("登陆失败!");
        }


    }
    public   String refresh(String authToken){
        String username = jwtTokenUtil.getSubject(authToken);
        if (username != null ) {
            return refresh(authToken,username);
        }
        return null;
    }
    public  String refresh(String authToken,String username){

        List<Object> redisTokenList =  RedisUtil.lGet(Constant.CODE_CACHE_TOKEN_USERNAME + "_" + username,0,-1);
        List<String> redisTokens=null;
        if(redisTokenList!=null){
            redisTokens=redisTokenList.stream().map(item->item.toString()).collect(Collectors.toList());
        }
        if (redisTokens!=null&&redisTokens.contains(authToken)) {
            //判断是否已经有刷新过的
            String token ;
            if(redisTokens.get(redisTokens.size()-1).equals(authToken)){
                token = jwtTokenUtil.refreshToken(authToken);
                log.info("authenticated user " + username + ",传来的token是最新的token.创建一个token并返回");
            }else  {
                //token 刷新
                token=redisTokens.get(redisTokens.size()-1);
                log.info("authenticated user " + username + ", 传来的token是旧的.返回最近刷新的token");
            }
            return token;
        }else{
            log.info("authenticated user " + username + ", token失效");
            //  return null;
            throw new UnauthorizedException("token失效");

        }

    }

    @Override
    public List<MenuDto> getMenusByUserId(Long userId) {
        return securityExMapper.getMenusByUserId(userId);
    }

}
